import pickle

from django.shortcuts import render
from django.utils.baseconv import base64
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import CartSerializer,CartSKUSerializer,CartDeleteSerializer
from django_redis import get_redis_connection
from goods.models import SKU

# 购物车
"""
先实现功能再优化

1. 登录用户的购物车保存在服务器， 未登录用户保存在cookie中
2. 我们需要保存商品的id，个数和选中的状态
3. cookie的保存格式： {sku_id:{count:5,selected:True},sku_id:{count:2,selected:False},}

    服务器(数据库/Redis)的保存格式： Redis中  内存，
            使用redis的原则： 尽量少的空间占用来实现功能
            sku_id,count,selected

            Hash:
            cart_8:
                {sku_id:count}
                {2:count}
                {3:count}
                {4:count}


            记录选中的id,没有在列表中的自然就是没有选中的
            cart_8
                [2,]



"""


class CartView(APIView):
    # 用户在进行提交购物车的时候，会进行token的认证，如果用户伪造token则不能进行
    # 购物车的添加
    # 我们先将token的认证 关掉
    def perform_authentication(self, request):
        pass

    def post(self, request):
        """
        # 1. 前段应该把sku_id,count必须传递过来，是否选中的状态是一个可选
        # 2. 我们就需要对前端提交的数据进行校验
        # 3. 校验完成之后，我们需要得到数据，然后将数据保存到指定的地方
        # 4. 获取登录的状态
        # 5. 登录用户保存在redis中
        # 6. 未登录用户保存在cookie中
        """

        # 1. 前段应该把sku_id,count必须传递过来，是否选中的状态是一个可选
        data = request.data
        # 2.我们需要对前端数据进行验证
        serializer = CartSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        # 效验之后的数据是没有问题的
        # 3.效验完成之后我们需要得到数据，然后把数据保存到指定地方
        # serializer.data 序列化操作
        sku_id = serializer.data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 4.获取登陆状态
        try:
            user = request.user
            print(user)
        except Exception:
            user = None
        # request.user.is_authenticated
        # 是否是认证用户（登陆用户）
        if user is not None and user.is_authenticated:
            # 5.登陆用户保存在redis中
            # 5.1  连接redis
            redis_conn = get_redis_connection('cart')
            # #保存数据/   添加hash数据
            # redis_conn.hset("cart_%s"%user.id,sku_id,count)
            # #hincrby实现增量
            # redis_conn.hincrby("cart_%s"%user.id,sku_id,count)
            # #添加选中集合set
            # if selected:
            #     redis_conn.sadd("cart_selected_%s"%user.id,sku_id)

            # 创建管道实例
            pl = redis_conn.pipeline()
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            pl.sadd('cart_selected_%s' % user.id, sku_id)
            # 让管道同意执行
            pl.execute()

            # 5.3返回响应
            return Response(serializer.data)

        else:
            #6.未登录的用户保存在cookie中
            #6.1 获取可能存在的cookie数据
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                #6.2如果数据存在就解码
                decode=base64.b64decode(cookie_str)
                cart = pickle.loads(decode)
                # {1:{count:4,selected:True}}
            else:
                #6.3如果数据不存在 就是空的
                cart={}
            #6.4 更新数据
            #如果之前添加过对应的商品应该进行累加
            if sku_id in cart:
                orgianl_count = cart[sku_id]['count']
                count += orgianl_count

            #cart
            cart[sku_id] = {
                'count':count,
                'selected':selected
            }
            #6.5 对最终的购物车数据进行编码
            dumps = pickle.dumps(cart)
            encode = base64.b64encode(dumps)
            value = encode.decode()
            #6.6返回响应
            response = Response(selected.data)
            # set_cookie的第三个参数 有效期 秒数
            response.set_cookie('cart', value, 3600 * 24)
            return response

    def get(self,request):
        """
        # 1. 我们判断用户的登录状态
        # 2. 登录用户从redis中获取数据
        # 3. 未登录用户从cookie中获取数据
        # 4.根据sku_id 查询商品的详细信息
        # 5.再将商品信息通过序列化器转换为字典
        """
        #1.判断用户的登陆状态
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            #2.登陆用户从redis中获取
            redis_conn = get_redis_connection('cart')
            #获取hash
            redis_cart = redis_conn.hgetall("cart_%s"%user.id)
            #{'sku_id':count,'sku_id:count','sku_id:count'}
            #获取选中的id
            redis_selected_ids = redis_conn.smembers("cart_selected_%s"%user.id)
            #将redis数据统一为 cart数据格式
            cart = {}
            ## cart = {sku_id: {count:xxx,selected:xxx}}
            # 对redis_cart 进行遍历     解包
            for sku_id,count in redis_cart.items():
                cart[sku_id.decode()] = {
                    'count':int(count),
                    'selected':sku_id in redis_selected_ids
                }


        else:
            #3.未登录用户从cookie中获取数据
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                #有cookie数据
                decode = base64.b64decode(cookie_str)
                cart = pickle.loads(decode)
            else:
                cart = {}
                # cart = {sku_id: {count:xxx,selected:xxx}}

        #4.根据sku_id 查询商品的详细信息
        ids = cart.keys()

        skus = SKU.objects.filter(pk__in = ids)
        # skus = [SKU,SKU,SKU]
        for sku in skus:
            #给对象动态添加属性
            sku.count = cart[str(sku.id)]['count']
            sku.selected = cart[str(sku.id)]['selected']

        #5.在将商品信息通过序列化器转换为字典
        serializer = CartSKUSerializer(skus,many=True)
        return Response(serializer.data)

    def put(self,request):
        """
        只要修改，就把所有的数据都提交
        幂等，就是将最终的结果提交给后端， 也得把最终结果返回给前端
        # 1. 前段应该把sku_id,count必须传递过来，是否选中的状态是一个可选
        # 2. 我们就需要对前端提交的数据进行校验
        # 3. 校验完成之后，我们需要得到数据，然后将数据保存到指定的地方
        # 4. 获取登录的状态
        # 5. 登录用户保存在redis中
        # 6. 未登录用户保存在cookie中
        """

        #1.前端应该把sku_id,count必须传递过来，是否选中的状态是一个可选
        data = request.data
        #2.对前端提交的数据进行验证
        serializer = CartSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        #3.效验完成后   需要得到数据，然后保存到指定地方
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        #4.获取登陆数据
        try:
            user = request.user
        except Exception:
            user = None
        # request.user.is_authenticated
        if user is not None and user.is_authenticated:
            #5.登陆用户保存在redis中
            redis_conn = get_redis_connection('cart')

            #幂等是将最终结果返回  更新数据  hash (非幂等容易导致数据丢失)
            redis_conn.hset('cart_%s'%user.id,sku_id,count)
            #set
            if selected:
                #选中就添加
                redis_conn.sadd('cart_%s'%user.id,sku_id)
            else:
                #未选中就删除
                redis_conn.srem('cart_%s'%user.id,sku_id)
            #把修改的结果返回
            return Response(serializer.data)

        else:
            #6.未登录保存在cookie中
            #6.1 获取数据  进行判断
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cart = {}
            #6.2进行数据更新
            # cart = {sku_id:{count:xxx,selected:xxxx}}
            if sku_id in cart:
                cart[sku_id] = {
                    'count':count,
                    'selected':selected
                }
            #6.3将新的cart数据进行处理     写入response
            value = base64.b64encode(pickle.dumps(cart)).decode()
            response = Response(serializer.data)
            response.set_cookie('cart',value,24*3600)
            return response

    def delete(self,request):
        """
         # 1. 前段应该把sku_id传递过来
        # 2. 我们就需要对前端提交的数据进行校验
        # 3. 校验完成之后，我们需要得到数据，然后将数据到指定的地方删除
        # 4. 获取登录的状态
        # 5. 登录用户保存在redis中
        # 6. 未登录用户保存在cookie中
        """
        #1.前端应该把sku_id传递过来
        data = request.data
        #2.我们对前端的数据进行效验
        serializer = CartDeleteSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        #3.效验完成后  我们需要得到数据，然后将数据到指定地方删除
        sku_id = serializer.data.get('sku_id')
        #4.获取登陆状态
        try:
            user = request.user
        except Exception:
            user = None
        # request.user.is_authenticated
        if user is not None and user.is_authenticated:
            #5.登陆用户保存在redis中
            redis_conn = get_redis_connection('cart')
            #先删hash  再删set 里面的
            redis_conn.hdel('cart_%s'%user.id,sku_id)
            redis_conn.srem('cart_selected_%s'%user.id,sku_id)

            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            #6.未登陆用户保存在cookie中
            cookie_str = request.COOKIES.get('cart')
            if cookie_str is not None:
                cart = pickle.loads(base64.b64decode(cookie_str))
            else:
                cart = {}
            response = Response(status=status.HTTP_204_NO_CONTENT)
            if sku_id in cart:
                del cart[sku_id]
                #再对数据进行编码，然后设置
                value = base64.b64encode(pickle.dumps(cart)).decode()
                response.set_cookie('cart',value,24*3600)

            return response



